package client.backend;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.StringTokenizer;

import server.conn.ConnectionThread;
import server.conn.user.UserConnectionThread;
import client.ClientConstants;
import server.order.Order;
/**
 * Class Connection is a Singleton class that will handle the communication to the server.
 * Also contains methods that return boolean if the operation was successful.
 */
public class Connection {
	
	private static Connection instance;
	private Socket echoSocket = null;
	private PrintWriter out = null;
	private BufferedReader in = null;
	
	/**
	 * Creates a new connection object.
	 */
	private Connection() {
		
	}
	
	/**
	 * Static method to return singleton instance.
	 * @return		Singleton instance of Connection.
	 */
	public static Connection getInstance() {
		if (instance == null) {
			instance = new Connection();
		}
		return instance;
	}
	
	/**
	 * Starts connection to server.
	 * @param host	Address of the server.
	 * @param port	Port used for connection.
	 */
	public void startConnection(String host, int port) {
		try {
            echoSocket = new Socket(host, port);
            out = new PrintWriter(echoSocket.getOutputStream(), true);
            in = new BufferedReader(new InputStreamReader(echoSocket.getInputStream()));
            System.out.println("Connection opened to host: "+host);
        } catch (UnknownHostException e) {
            System.err.println("Don't know about host: "+host);
            System.exit(1);
        } catch (IOException e) {
            System.err.println("Couldn't get I/O for the connection to: "+host);
            System.exit(1);
        }
	}
	
	/**
	 * Executes command and reads and returns the next line in BufferedReader without skipping the OK.
	 * @param statement	Statement to be sent to the server.
	 * @return			First line of response from server.
	 * @throws IOException
	 */
	private String readNoSkip(String statement) throws IOException {
		out.println(statement);
		return in.readLine();
	}
	
	/**
	 * Returns next line in BufferedReader without skipping the OK.
	 * @return	Next line in BufferedReader.
	 * @throws IOException
	 */
	private String readNoSkip() throws IOException {
		return in.readLine();
	}
	
	/**
	 * Executes statement and returns first line in BufferedReader, as well as clearing second line in BufferedReader.
	 * @param statement	Statement to be executed.
	 * @return			Next line in BufferedReader.
	 * @throws IOException
	 */
	public String readFromServer(String statement) throws IOException {
		out.println(statement);
		String ret = in.readLine();
		in.readLine();
		return ret;
	}
	
	/**
	 * Reads first line from BufferedReader and clears second line.
	 * @return	Next line in BufferedReader.
	 * @throws IOException
	 */
	public String readFromServer() throws IOException {
		String ret = in.readLine();
		in.readLine();
		return ret;
	}
	
	/**
	 * Executes statement and returns an array of responses from BufferedReader. Uses a number to get length of array.
	 * @param statement	Statement to be executed.
	 * @return			Array of lines from BufferedReader.
	 */
	public String[] readArray(String statement) {
		out.println(statement);
		int arrayLength = 0;
		try {
			arrayLength = new Integer(in.readLine());
		} catch (NumberFormatException e1) {
			
		} catch (IOException e1) {

		}
		String[] retArray = new String[arrayLength];
		
		String temp;
		int counter = 0;
		try {
			while(!(temp = in.readLine()).equals(ConnectionThread.OK)) {
				retArray[counter++] = temp;
			}
		} catch (IOException e) {
			retArray[counter++] = null;
		}
		
		return retArray;
	}
	
	/**
	 * Executes a statement and returns every response until server sends "OK".
	 * @param statement	Statement to be executed.
	 * @return			Array of lines read from server.
	 * @throws IOException
	 */
	public String[] readLines(String statement) throws IOException {
		out.println(statement);
		String capture = "";
		while(true){
			String currline = in.readLine();
			if(currline==ConnectionThread.OK || currline==ConnectionThread.ERROR){
				capture+=currline;
				break;
			}
			capture+=currline+"\n";
		}
		StringTokenizer stok = new StringTokenizer(capture,"\n");
		String ret[] = new String[stok.countTokens()];
		int index=0;
		while(stok.hasMoreElements()){
			ret[index]=stok.nextToken();
		}
		return ret;
	}
	
	/**
	 * Write line to Server without reading anything.
	 * @param statement	Statement to be executed.
	 * @throws IOException
	 */
	public void writeToServer(String statement) throws IOException {
		out.println(statement);
	}
	
	/**
	 * Attempts to login to server and returns whether or not it was successful.
	 * @param username	Username of client trying to connect.
	 * @param password	Password of client trying to connect.
	 * @return			Returns whether or not login was successful.
	 */
	public String login(String username, String password){
		startConnection(ClientConstants.host, ClientConstants.port);
		
		try{
			readNoSkip();
			String response = readNoSkip(ConnectionThread.LOGIN+ConnectionThread.SPLIT+username+ConnectionThread.SPLIT+password);

			return response;
		} catch (IOException e){
			return "ERROR";
		}
	}
	
	/**
	 * Attempts to log admin on to server and returns whether or not it was successful.
	 * @param username	Username of client trying to connect.
	 * @param password	Password of client trying to connect.
	 * @return			Returns whether or not login was successful.
	 */
	public boolean adminLogin(String username, String password){
		startConnection(ClientConstants.host, ClientConstants.adminPort);
		
		try{
			readNoSkip();
			String response = readNoSkip(ConnectionThread.LOGIN+ConnectionThread.SPLIT+username+ConnectionThread.SPLIT+password);

			if(response.equals(ConnectionThread.OK))
				return true;
		} catch (IOException e){
			return false;
		}
		return false;
	}

	/**
	 * Tries to close to connection and quit.
	 */
	public void quit() {
		try {
			writeToServer("0");
			System.exit(0);
		}
		catch (Exception e) {
			System.err.println("unable to close connection");
		}
	}
	
	/**
	 * Attempts to logout and returns if successful.
	 * @return	Whether or not login was successful.
	 */
	public boolean logout() {
		try {
			if (readNoSkip("0").equals(ConnectionThread.BYE))
				if (readNoSkip().equals(ConnectionThread.OK))
					return true;
		}
		catch (Exception e) { 
			System.err.println("unable to close connection");
		}
		return false;
	}
}
